﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using FBProject.BusinessLogic.MSProjectProxies;

namespace FBProject.BusinessLogic
{
    public class TaskSynchronizer
    {
        public event ProgressChangedEventHandler ProgressChanged;

        private void ReportProgress(int progressPercentage, object userState)
        {
            if (this.ProgressChanged != null)
            {
                int overallProgressPercentage = (int)(progressPercentage * (double)(this.ProgressMax - this.ProgressMin) / 100.0);
                var eventArgs = new ProgressChangedEventArgs(overallProgressPercentage, userState);
                this.ProgressChanged(this, eventArgs);
            }
        }

        private bool CancellationPending { get; set; }
        private MSProjectProxy MSProjectProxy { get; set; }
        private int ProgressMin { get; set; }
        private int ProgressMax { get; set; }

        public TaskSynchronizer(MSProjectProxy msProjectProxy, int progressMin, int progressMax)
        {
            if (msProjectProxy == null)
                throw new ArgumentNullException("msProjectProxy");

            this.CancellationPending = false;
            this.MSProjectProxy = msProjectProxy;
            this.ProgressMin = progressMin;
            this.ProgressMax = progressMax;
        }

        public void Cancel()
        {
            this.CancellationPending = true;
        }

        public IList<TaskDifference> Synchronize(IDictionary<string, ExternalTask> externalTasks)
        {
            if (externalTasks == null)
                throw new ArgumentNullException("externalTasks");

            var taskDifferences = new List<TaskDifference>();

            this.MSProjectProxy.SaveStateAndPrepareForSynchronization();

            ApplicationConfiguration.LastMSProject = this.MSProjectProxy.FullName;

            if (!this.CancellationPending)
            {
                this.ReportProgress(5, "Expanding all subtasks in MS Project");
                this.MSProjectProxy.ExpandAllSubtasks();
            }
            if (!this.CancellationPending)
            {
                this.ReportProgress(10, "Removing previous highlighting");
                this.RemoveDifferencesHighlighting();
            }
            if (!this.CancellationPending)
            {
                this.ReportProgress(15, "Comparing external tasks with tasks in MS Project");
                taskDifferences = this.GetTaskDifferences(externalTasks);
            }
            if (!this.CancellationPending)
            {
                this.ReportProgress(70, "Highlighting task differences");
                this.HighlightTaskDifferences(taskDifferences);
            }
            if (!this.CancellationPending)
            {
                this.ReportProgress(85, "Collapsing subtasks");
                this.MSProjectProxy.CollapseSubtasksNotInProgress();
            }
            if (!this.CancellationPending)
            {
                this.ReportProgress(95, "Going to first unfinished task");
                this.MSProjectProxy.GoToFirstUnfinishedTask();
            }

            this.MSProjectProxy.RestoreStateAfterSynchronization();

            return taskDifferences;
        }

        private List<TaskDifference> GetTaskDifferences(IDictionary<string, ExternalTask> externalTasks)
        {
            var taskDifferences = new List<TaskDifference>();
            var ignoredTaskRowNumbers = new List<int>();
            var completedTaskRowNumbers = new List<int>();
            foreach (var task in this.MSProjectProxy.Tasks)
            {
                if (this.CancellationPending)
                    break;

                if (task.IgnoreForComparison)
                {
                    ignoredTaskRowNumbers.Add(task.Id);
                }
                else if (task.Completed)
                {
                    completedTaskRowNumbers.Add(task.Id);
                }

                if (task.IsExternalTask)
                {
                    ExternalTask externalTask;
                    if (externalTasks.TryGetValue(task.TaskNumber, out externalTask))
                    {
                        if ((!task.Completed) || (task.Completed == externalTask.Completed))
                        {
                            externalTask.NewTask = false;
                            if (!task.IgnoreForComparison)
                            {
                                taskDifferences.AddRange(task.GetTaskDifferences(externalTask));
                            }
                        }
                    }
                    else if ((!task.IgnoreForComparison) && (!task.Completed))
                    {
                        taskDifferences.Add(new TaskDifference(task.Id, task.FBCaseDescription, task.TaskHyperlink, TaskDifferenceType.DeletedTask));
                    }
                }
            }

            this.HighlightCompletedTask(completedTaskRowNumbers.ToArray());
            this.HighlightIgnoredTask(ignoredTaskRowNumbers.ToArray());
            
            foreach (var externalTask in externalTasks.Values)
            {
                if (this.CancellationPending)
                    break;

                if (externalTask.NewTask && !externalTask.Completed)
                {
                    var newTask = this.MSProjectProxy.AddTask(externalTask);
                    taskDifferences.Add(new TaskDifference(newTask.Id, newTask.FBCaseDescription, newTask.TaskHyperlink, TaskDifferenceType.NewTask));
                }
            }

            return taskDifferences;
        }


        private void RemoveDifferencesHighlighting()
        {
            this.MSProjectProxy.ChangeSheetCellColor(-16777216);
        }

        private void HighlightIgnoredTask(int[] rows)
        {
            this.MSProjectProxy.ChangeRowCellColor(rows, 9944516);
        }

        private void HighlightCompletedTask(int[] rows)
        {
            this.MSProjectProxy.ChangeRowCellColor(rows, 14211288);
        }

        private void HighlightTaskDifferences(IList<TaskDifference> taskDifferences)
        {
            if (taskDifferences != null)
            {
                foreach (var taskDifference in taskDifferences)
                {
                    if (this.CancellationPending)
                        break;

                    this.MSProjectProxy.ChangeCellCellColor(taskDifference.TaskId, taskDifference.MSProjectColumnName, 9421050);
                }
            }
        }

    }
}
